iT邦幫忙

0

API 使用動態金鑰加密資料

  • 分享至 

  • xImage
  •  

api-dynamic-key-encryption

在 API 通訊加密中,使用 固定金鑰 (Static Key Wrapping) 雖然實作簡單,但一旦金鑰外洩,後果嚴重。若能在每次登入時動態產生金鑰 (Dynamic Key Wrapping),可有效縮短攻擊面並降低風險。本文將介紹固定金鑰與動態金鑰的差異,並示範如何在 Bee.NET 框架中設定與實作動態金鑰。

📦 範例程式:jsonrpc-sample


1️⃣ 固定金鑰 vs 動態金鑰差異

固定金鑰 (Static Key Wrapping)

  • 概念:所有用戶端共用同一把 API 加密金鑰,並儲存在後端設定檔。
  • 優點
    • 架構簡單,不需維護 Session 狀態。
    • 適合內網或低風險系統。
  • 缺點
    • 金鑰一旦外洩,歷史封包可被回溯解密。
    • 必須人工輪換金鑰,維運成本高。

動態金鑰 (Dynamic Key Wrapping)

  • 概念:每次用戶登入時,系統隨機產生新的 API 加密金鑰,並與 Session 綁定。
  • 優點
    • 金鑰外洩僅影響當前 Session,歷史封包不易回溯解密。
    • 金鑰自動輪換,安全性高。
  • 缺點
    • 必須維護 Session 狀態,實作稍複雜。
    • 登入流程多一次金鑰產生與加解密。

2️⃣ Bee.NET 動態金鑰設定方式

透過 BeeSettingsEditor 工具編輯 SystemSettings.xml,只需設定 <ApiEncryptionKeyProvider>

<SystemSettings>
  <BackendConfiguration>
    <!--
        ApiEncryptionKeyProvider:
        - Bee.Business.StaticApiEncryptionKeyProvider, Bee.Business   (靜態金鑰)
        - Bee.Business.DynamicApiEncryptionKeyProvider, Bee.Business  (動態金鑰)
    -->
    <ApiEncryptionKeyProvider>
      Bee.Business.DynamicApiEncryptionKeyProvider, Bee.Business
    </ApiEncryptionKeyProvider>
  </BackendConfiguration>
</SystemSettings>

設定 DynamicApiEncryptionKeyProvider 後,系統會在登入時自動產生 Session 專屬金鑰,並於 API 加解密時自動套用。


3️⃣ Login 方法支援靜態與動態金鑰

Bee.NETLogin 方法在登入時都會產生金鑰並存入 SessionInfo,差別在於後續是否使用:

  • 靜態金鑰模式:使用設定檔共用金鑰,SessionInfo 的金鑰不會被使用。
  • 動態金鑰模式:登入時產生隨機金鑰並寫入 SessionInfo,API 透過 AccessToken 取出對應金鑰。

Login 方法範例

public virtual LoginResult Login(LoginArgs args)
{
    // 1. 驗證帳密
    if (!AuthenticateUser(args))
        throw new UnauthorizedAccessException("Invalid username or password.");

    // 2. 依設定產生靜態或動態金鑰
    byte[] encryptionKey = BackendInfo.ApiEncryptionKeyProvider.GenerateKeyForLogin();

    // 3. 建立 SessionInfo 並存入快取
    var sessionInfo = new SessionInfo
    {
        AccessToken = Guid.NewGuid(),
        UserID = args.UserId,
        ExpiredAt = DateTime.UtcNow.AddHours(1),
        ApiEncryptionKey = encryptionKey
    };
    CacheFunc.SetSessionInfo(sessionInfo);

    // 4. 用 RSA 公鑰加密金鑰回傳
    string encryptedKey = RsaCryptor.EncryptWithPublicKey(
        Convert.ToBase64String(encryptionKey),
        args.ClientPublicKey
    );

    return new LoginResult
    {
        AccessToken = sessionInfo.AccessToken,
        ExpiredAt = sessionInfo.ExpiredAt,
        ApiEncryptionKey = encryptedKey
    };
}

4️⃣ 金鑰提供者實作比較

動態金鑰提供者 DynamicApiEncryptionKeyProvider

public class DynamicApiEncryptionKeyProvider : IApiEncryptionKeyProvider
{
    public byte[] GetKey(Guid accessToken)
    {
        if (BaseFunc.IsEmpty(accessToken))
            throw new UnauthorizedAccessException("Access token is required.");

        var sessionInfo = CacheFunc.GetSessionInfo(accessToken);
        return sessionInfo?.ApiEncryptionKey
            ?? throw new UnauthorizedAccessException("Session key not found or expired.");
    }

    public byte[] GenerateKeyForLogin()
    {
        return AesCbcHmacKeyGenerator.GenerateCombinedKey();
    }
}

靜態金鑰提供者 StaticApiEncryptionKeyProvider

public class StaticApiEncryptionKeyProvider : IApiEncryptionKeyProvider
{
    public byte[] GetKey(Guid accessToken)
    {
        return BackendInfo.ApiEncryptionKey
            ?? throw new InvalidOperationException("BackendInfo.ApiEncryptionKey is not initialized.");
    }

    public byte[] GenerateKeyForLogin()
    {
        return GetKey(Guid.Empty);
    }
}

5️⃣ JSON-RPC 請求參數範例

以下為 未加密加密 格式的範例,示範如何傳遞 Employee.Hello 方法的參數:

(1) 未加密格式

{
  "jsonrpc": "2.0",
  "method": "Employee.Hello",
  "params": {
    "value": {
      "$type": "Custom.Define.HelloArgs, Custom.Define",
      "userName": "Jeff"
    }
  },
  "id": "85b2c2c3-9854-4eb9-b6dc-6a82a9165fc3"
}

(2) 加密格式 (Encrypted)

{
  "jsonrpc": "2.0",
  "method": "Employee.Hello",
  "params": {
    "format": "Encrypted",
    "value": {
      "$type": "System.Byte[], System.Private.CoreLib",
      "$value": "EAAAAFw44iP8acRR8V6gK6A4g8UwAAAAU6rvBwZkdXaboglEilgv8rSX2gaxK3phfZUW1tJCiNsPoCNt9hPJ1VLO8hnJe9eqJ8NanxPrstyOssDJV8GQjderfsBtBfGer1WcdgnGYy0="
    },
    "type": "Custom.Define.HelloArgs, Custom.Define"
  },
  "id": "85b2c2c3-9854-4eb9-b6dc-6a82a9165fc3"
}

formatEncrypted 時,value 會變成經 AES + HMAC 加密後的 Base64 字串,並透過動態金鑰進行解密。


6️⃣ 動態金鑰完整流程圖

api-dynamic-key-encryption-flowchart


✨ 總結

  • Login 方法透過 IApiEncryptionKeyProvider 介面即可同時支援 靜態動態金鑰,由設定檔決定模式。
  • 動態金鑰模式:登入時自動產生 Session 專屬金鑰,降低回溯解密風險。
  • 靜態金鑰模式:實作簡單,但外洩影響全面,建議僅用於低風險或內網系統。
  • 搭配 JSON-RPC 加密傳輸,可有效保護 API 傳遞的參數資料。

📘 HackMD 原文筆記:
👉 https://hackmd.io/@jeff377/api-dynamic-key-encryption

📢 歡迎轉載,請註明出處
📬 歡迎追蹤我的技術筆記與實戰經驗分享
FacebookHackMDGitHubNuGet


圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言